package com.tramp.frame.server.base;

import java.util.*;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import com.tramp.basic.service.OperateLogService;
import com.tramp.frame.server.base.dao.BaseDao;
import com.tramp.frame.server.base.field.BaseField;
import com.tramp.frame.server.exception.GenericException;
import com.tramp.utils.JSONUtils;
import com.tramp.utils.ShiroUtils;

/**
 * Created by chen on 2017/10/21.
 */
public class BaseService<T extends BaseEntity> {

	public static final String EXCEPTION_MSG_INSERT_FAIL = "插入数据失败";
	public static final String EXCEPTION_MSG_ID_IS_EMPTY_WHEN_UPDATE = "更新数据时ID不可为空！";
	//public static final String EXCEPTION_MSG_FIELDS_IS_EMPTY_WHEN_UPDATE = "更新数据时字段不可为空！";
	public static final String EXCEPTION_MSG_UPDATE_FAIL = "更新数据失败";

	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	private BaseDao<T> baseDao;

	@Autowired
	private OperateLogService operateLogService;

	private final String entityName;

	@Autowired
	public BaseService(BaseDao baseDao) {
		entityName = this.getClass().getSimpleName().replace("Service", "");
		this.baseDao = baseDao;
	}

	private String getUUID() {
		return UUID.randomUUID().toString();
	}

	private void setRecordInfo(Recordable record, Boolean isInsert, Date now, String user) {
		record.setUpdateTime(now);
		record.setUpdateUser(user);
		if (isInsert) {
			record.setCreateTime(now);
			record.setCreateUser(user);
		}
	}

	public T get(String id) {
		if (StringUtils.isBlank(id)) {
			return null;
		}
		return this.baseDao.get(id);
	}

	public T insert(T entity) {
		if (StringUtils.isBlank(entity.getId())) {
			entity.setId(getUUID());
		}
		if (entity instanceof Recordable) {
			setRecordInfo((Recordable) entity, true, new Date(), ShiroUtils.getUserName());
		}
		if (baseDao.insert(entity) != 1) {
			logger.error("insert fail,Output count is not equal to 1.entity:{}", JSONUtils.toJson(entity));
			throw new GenericException(EXCEPTION_MSG_INSERT_FAIL);
		}
		operateLogService.addOperateLogData("insert", entityName, entity);
		return entity;
	}

	public List<T> insert(List<T> entitys) {
		if (CollectionUtils.isEmpty(entitys)) {
			return entitys;
		}
		Date now = null;
		String admin = null;
		Boolean isRecord = false;
		if (entitys.iterator().next() instanceof Recordable) {
			now = new Date();
			admin = ShiroUtils.getUserName();
			isRecord = true;
		}

		for (T entity : entitys) {
			if (StringUtils.isBlank(entity.getId())) {
				entity.setId(getUUID());
			}
			if (isRecord) {
				setRecordInfo((Recordable) entity, true, now, admin);
			}
		}
		if (baseDao.insertBatch(entitys) != entitys.size()) {
			logger.error("insert fail,The count of inputs and outputs is not equal.entitys:{}", JSONUtils.toJson(entitys));
			throw new GenericException(EXCEPTION_MSG_INSERT_FAIL);
		}
		operateLogService.addOperateLogData("insertBatch", entityName, entitys);
		return entitys;
	}

	public T update(T entity) {
		if (StringUtils.isBlank(entity.getId())) {
			logger.warn("update fail,id is blank.entity:{}", JSONUtils.toJson(entity));
			throw new GenericException(EXCEPTION_MSG_ID_IS_EMPTY_WHEN_UPDATE);
		}
		if (entity instanceof Recordable) {
			setRecordInfo((Recordable) entity, false, new Date(), ShiroUtils.getUserName());
		}
		if (baseDao.update(entity) != 1) {
			logger.error("update fail,Output count is not equal to 1.entity:{}", JSONUtils.toJson(entity));
			throw new GenericException(EXCEPTION_MSG_UPDATE_FAIL);
		}
		operateLogService.addOperateLogData("update", entityName, entity);
		return entity;
	}

	public List<T> update(List<T> entitys) {
		if (CollectionUtils.isEmpty(entitys)) {
			return entitys;
		}
		Date now = null;
		String admin = null;
		Boolean isRecord = false;
		if (entitys.iterator().next() instanceof Recordable) {
			now = new Date();
			admin = ShiroUtils.getUserName();
			isRecord = true;
		}

		for (T entity : entitys) {
			if (StringUtils.isBlank(entity.getId())) {
				logger.warn("update fail,id is blank.entity:{}", JSONUtils.toJson(entity));
				throw new GenericException(EXCEPTION_MSG_ID_IS_EMPTY_WHEN_UPDATE);
			}
			if (isRecord) {
				setRecordInfo((Recordable) entity, false, now, admin);
			}
		}
		if (baseDao.updateBatch(entitys) != entitys.size() * 2) {
			logger.error("update fail,The count of inputs and outputs is not equal.entitys:{}", JSONUtils.toJson(entitys));
			throw new GenericException(EXCEPTION_MSG_UPDATE_FAIL);
		}
		operateLogService.addOperateLogData("updateBatch", entityName, entitys);
		return entitys;
	}

	public T updateSelective(T entity) {
		if (StringUtils.isBlank(entity.getId())) {
			logger.warn("updateSelective fail,id is blank.entity:{}", JSONUtils.toJson(entity));
			throw new GenericException(EXCEPTION_MSG_ID_IS_EMPTY_WHEN_UPDATE);
		}
		if (entity instanceof Recordable) {
			setRecordInfo((Recordable) entity, false, new Date(), ShiroUtils.getUserName());
		}
		if (baseDao.updateSelective(entity) != 1) {
			logger.error("updateSelective fail,Output count is not equal to 1.entity:{}", JSONUtils.toJson(entity));
			throw new GenericException(EXCEPTION_MSG_UPDATE_FAIL);
		}
		operateLogService.addOperateLogData("updateSelective", entityName, entity);

		return entity;
	}

	public T insertOrUpdate(T entity) {
		if (StringUtils.isBlank(entity.getId())) {
			//新增
			this.insert(entity);
		}
		else {
			//更新
			this.update(entity);
		}
		return entity;
	}

	public List<T> insertOrUpdate(List<T> entitys) {
		List<T> insertList = new ArrayList<>();
		List<T> updateList = new ArrayList<>();
		for (T entity : entitys) {
			if (StringUtils.isBlank(entity.getId())) {
				//新增
				insertList.add(entity);
			}
			else {
				//更新
				updateList.add(entity);
			}
		}
		this.insert(insertList);
		this.update(updateList);
		return entitys;
	}

	public void delete(String id) {
		if (StringUtils.isBlank(id)) {
			return;
		}
		List<String> ids = new ArrayList<>();
		ids.add(id);
		delete(ids);
	}

	public void delete(Collection<String> ids) {
		if (CollectionUtils.isEmpty(ids)) {
			return;
		}
		for (String id : ids) {
			if (StringUtils.isBlank(id)) {
				logger.warn("delete fail,ids exists empty.ids:{}", JSONUtils.toJson(ids));
				throw new GenericException("删除失败,存在空ID");
			}
		}
		if (baseDao.delete(ids) != ids.size()) {
			logger.error("delete fail,The count of inputs and outputs is not equal.ids:{}", JSONUtils.toJson(ids));
			throw new GenericException("删除失败");
		}
		operateLogService.addOperateLogData("delete", entityName, ids);

	}

	public void deleteByEntity(Collection<T> entitys) {
		if (CollectionUtils.isEmpty(entitys)) {
			return;
		}
		List<String> ids = new ArrayList<>();
		for (BaseEntity entity : entitys) {
			if (null == entity || StringUtils.isBlank(entity.getId())) {
				logger.warn("deleteByEntity fail,ids exists empty.entitys:{}", JSONUtils.toJson(entitys));
				throw new GenericException("删除失败,存在空ID");
			}
			ids.add(entity.getId());
		}
		delete(ids);
	}

	public List<T> listByEntity(T record) {
		return baseDao.listByEntity(record);
	}

	public void updateField(T entity, BaseField entityField) {
		if (null == entityField || StringUtils.isBlank(entity.getId())) {
			logger.warn("updateField fail,id is blank.entity:{}", JSONUtils.toJson(entityField));
			throw new GenericException(EXCEPTION_MSG_ID_IS_EMPTY_WHEN_UPDATE);
		}
		Map<String, String> updateFieldMap = entityField.getUpdateFieldMap();
		if (MapUtils.isEmpty(updateFieldMap)) {
			return;
		}
		if (entity instanceof Recordable) {
			setRecordInfo((Recordable) entity, false, new Date(), ShiroUtils.getUserName());
			updateFieldMap.put("updateUser", "");
			updateFieldMap.put("updateTime", "");
		}
		Object[] logDataParam = new Object[2];
		logDataParam[0] = entity;
		logDataParam[1] = entityField;
		if (baseDao.updateField(entity, updateFieldMap) != 1) {
			logger.error("updateField fail,Output count is not equal to 1.entity:{}", JSONUtils.toJson(logDataParam));
			throw new GenericException(EXCEPTION_MSG_UPDATE_FAIL);
		}
		operateLogService.addOperateLogData("updateField", entityName, logDataParam);
	}
}
